package services.entrega;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.springframework.transaction.annotation.Transactional;

import services.venta.VentaService;
import util.SearchOperator;
import util.assembly.EntregaAssembly;
import util.assembly.VentaAssembly;
import bo.Entrega;
import bo.Factura;
import bo.ItemFactura;
import bo.MovimientoStock;
import bo.PersistentObject;
import bo.Producto;
import bo.Venta;
import dao.EntregaDAO;
import dao.MovimientoStockDAO;
import dao.ProductoDAO;
import dto.EntregaDTO;
import dto.SearchDTO;
import dto.VentaDTO;
import exceptions.CannotDeleteException;
import exceptions.CannotSaveException;
import exceptions.EntityNotFoundException;

public class EntregaService {
	
	private VentaService ventaService;
	
	private MovimientoStockDAO movimientoStockDAO;
	
	private EntregaDAO entregaDAO;
	
	private ProductoDAO productoDAO;
	
	private Venta venta;
	
	/**
	 * Guarda un entrega
	 * @param entregaDTO	DTO de entrega
	 */
	@Transactional
	public String save(EntregaDTO entregaDTO) throws CannotSaveException {
		
		Entrega entrega = EntregaAssembly.toBO(entregaDTO);
		
		agregarMovimientoStock(entrega);
		
		String id = entregaDAO.save(entrega);
		if (id == null) {
			throw new CannotSaveException();
		} 
		return id;
	}

	
	private void agregarMovimientoStock(Entrega entrega) throws CannotSaveException {
		Object id = null;
		String entregaId = entrega.getId();
		Entrega old = entregaDAO.findById(entregaId);
		String oldEstado = old.getEstado();
		String newEstado = entrega.getEstado();
		entregaDAO.getHibernateTemplate().flush();
		entregaDAO.getHibernateTemplate().evict(entrega);
		entregaDAO.getHibernateTemplate().evict(old);
		if (Entrega.ESTADO_PENDIENTE.equals(oldEstado) && Entrega.ESTADO_ENTREGADA.equals(newEstado)) {
			
			// Movimiento de stock generado por una entrega ENTREGADA
			MovimientoStock movimientoStock = new MovimientoStock();
			movimientoStock.setFecha(new java.util.Date());
			Integer cantidad = getCantidadVentaRelacionada(entregaId);
			movimientoStock.setCantidad(cantidad);
			movimientoStock.setEntrega(entrega);
			movimientoStockDAO.getHibernateTemplate().clear();
			movimientoStockDAO.getHibernateTemplate().flush();
			movimientoStockDAO.getHibernateTemplate().evict(entrega);
			movimientoStockDAO.getHibernateTemplate().evict(old);
			id = movimientoStockDAO.save(movimientoStock);
			if (id == null) {
				throw new CannotSaveException();
			}
			
			// descontar stock de productos entregados
			Factura factura = venta.getFactura();
			Set<ItemFactura> items2 = factura.getItems();
			for (ItemFactura itemFactura : items2) {
				Producto producto = itemFactura.getProducto();
				producto.getStock().setCantidad(producto.getStock().getCantidad() - itemFactura.getCantidad());
				productoDAO.save(producto);
				productoDAO.getHibernateTemplate().flush();
			}
		}
	}
	
	private Integer getCantidadVentaRelacionada(String entregaId) {
		Integer acum = 0;
		
		SearchDTO searchDTO = new SearchDTO();
		searchDTO.addCriteria("entrega.id", entregaId, SearchOperator.EQUAL);
		List<VentaDTO> search = ventaService.search(searchDTO);
		if (search != null && search.size() > 0) {
			venta = VentaAssembly.toBO(search.get(0));
			
			Set<ItemFactura> itemFacturaSet = venta.getFactura().getItems();
			for (ItemFactura itemFactura : itemFacturaSet) {
				acum += itemFactura.getCantidad();
			}
			return acum * -1;
		}
		return acum;
	}
	
	
	/**
	 * Borra un entrega
	 * @param id	ID del entrega a borrar
	 * @throws CannotDeleteException
	 */
	@Transactional
	public void delete(String id) throws CannotDeleteException {
		
		Entrega entrega = entregaDAO.findById(id);
		
		entregaDAO.delete(entrega);
	}
	
	
	@Transactional
	public List<EntregaDTO> search(SearchDTO searchDTO) {
		
		List<EntregaDTO> lista = new ArrayList<EntregaDTO>();
		
		List<PersistentObject> entregas = entregaDAO.search(Entrega.class, searchDTO);
		for (PersistentObject persistentObject : entregas) {
			lista.add(EntregaAssembly.toDTO((Entrega)persistentObject));
		}
		return lista;
	}
	
	
	
	/**
	 * Obtiene todos los producots
	 * @return	la lista de entregas
	 */
	@Transactional
	public List<EntregaDTO> findAll() {
		
		List<EntregaDTO> lista = new ArrayList<EntregaDTO>();
		
		List<PersistentObject> entregas = entregaDAO.findAll();
		for (PersistentObject persistentObject : entregas) {
			lista.add(EntregaAssembly.toDTO((Entrega)persistentObject));
		}
		return lista;
	}
	
	
	@Transactional
	public List<EntregaDTO> findAll(String order, boolean asc) {
		
		List<EntregaDTO> lista = new ArrayList<EntregaDTO>();
		
		List<PersistentObject> entregas = entregaDAO.findAll(Entrega.class, order, asc);
		for (PersistentObject persistentObject : entregas) {
			lista.add(EntregaAssembly.toDTO((Entrega)persistentObject));
		}
		return lista;	
	}
	
	
	/**
	 * Obtiene un entrega segun ID
	 * @param id	el ID
	 * @return el entrega
	 */
	@Transactional
	public EntregaDTO findById(String id) throws EntityNotFoundException {
		
		Entrega entrega = entregaDAO.findById(id);
		
		if (entrega == null) {
			throw new EntityNotFoundException();
		}
		
		EntregaDTO entregaDTO = EntregaAssembly.toDTO(entrega);
		return entregaDTO;
	}

	
	
	public Venta getVenta() {
		return venta;
	}

	public void setVenta(Venta venta) {
		this.venta = venta;
	}

	public EntregaDAO getEntregaDAO() {
		return entregaDAO;
	}

	public void setEntregaDAO(EntregaDAO entregaDAO) {
		this.entregaDAO = entregaDAO;
	}

	public ProductoDAO getProductoDAO() {
		return productoDAO;
	}

	public void setProductoDAO(ProductoDAO productoDAO) {
		this.productoDAO = productoDAO;
	}

	public VentaService getVentaService() {
		return ventaService;
	}

	public void setVentaService(VentaService ventaService) {
		this.ventaService = ventaService;
	}

	public MovimientoStockDAO getMovimientoStockDAO() {
		return movimientoStockDAO;
	}

	public void setMovimientoStockDAO(MovimientoStockDAO movimientoStockDAO) {
		this.movimientoStockDAO = movimientoStockDAO;
	}

}
